﻿using System;
using System.Collections.Generic;
using System.Text;

using Avanade.ViddlerDotNet.DataContracts;
using System.Security.Authentication;
using System.IO;

namespace Avanade.ViddlerDotNet
{
    public struct ViddlerMethod
    {
        public const string GetInfo = "viddler.api.getInfo";
        public const string AuthenticateUser = "viddler.users.auth";
        public const string GetUserProfile = "viddler.users.getProfile";
        public const string SetUserProfile = "viddler.users.setProfile";
        public const string SetUserOptions = "viddler.users.setOptions";
        public const string GetRecordToken = "viddler.videos.getRecordToken";
        public const string UploadVideo = "viddler.videos.upload";
        public const string GetVideoStatus = "viddler.videos.getStatus";
        public const string GetVideoDetails = "viddler.videos.getDetails";
        public const string GetVideoDetailsByUrl = "viddler.videos.getDetailsByUrl";
        public const string SetPermalink = "viddler.videos.setPermalink";
        public const string GetVideosByUser = "viddler.videos.getByUser";
        public const string GetVideosByTag = "viddler.videos.getByTag";
        public const string GetFeaturedVideos = "viddler.videos.getFeatured";
        public const string AddComment = "viddler.videos.comments.add";
        public const string RemoveComment = "viddler.videos.comments.remove";
    }

    public class Viddler
    {
        private string sessionID;
        private readonly string apiKey = string.Empty;
        private readonly string baseUrl = string.Empty;

        public string SessionID
        {
            get { return sessionID; }
        }

        public bool IsAuthenticated
        {
            get
            {
                if (sessionID == null)
                    return false;
                else
                    return true;
            }
        }

        /// <summary>
        /// Default constructor which uses values for the api key and base url from the .config file
        /// </summary>
        public Viddler()
        {
           // use the default settings from config 
            apiKey = Settings.CurrentSettings.ApiKey;
            baseUrl = Settings.CurrentSettings.BaseUrl;
        }

        /// <summary>
        /// This constructor makes it possible to set up a Viddler connection without neccessarily have a .config file
        /// </summary>
        /// <param name="apiKey"></param>
        /// <param name="baseUrl"></param>
        public Viddler(string apiKey, string baseUrl)
        {
            this.apiKey = apiKey;
            this.baseUrl = baseUrl;
        }

        /// <summary>
        /// Method viddler.api.getInfo
        /// Creates a Viddler account. *Restricted to only qualified API keys. 
        /// </summary>
        public GetInfoResponse GetInfo()
        {
            ViddlerRequest request = new ViddlerRequest(ViddlerMethod.GetInfo, apiKey, baseUrl);
            ViddlerResponse response = request.GetResponse();

            return response.GetResponseObject<GetInfoResponse>();
        }

        /// <summary>
        /// Method viddler.users.register 
        /// Creates a Viddler account. *Restricted to only qualified API keys. 
        /// </summary>
        public void RegisterUser()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Method viddler.users.auth 
        /// Authenticates an account with Viddler. Returned sessionid is valid for 5 minutes (may change in the future).
        /// Every method request which contains valid sessionid, renews its validity time. 
        /// </summary>
        public void AuthenticateUser(string userName, string password)
        {
            ViddlerRequest request = new ViddlerRequest(ViddlerMethod.AuthenticateUser, apiKey, baseUrl);
            request.Parameters.Add("user", userName);
            request.Parameters.Add("password", password);
            ViddlerResponse response = request.GetResponse();

            AuthenticateUserResponse authenticateUserResponse = response.GetResponseObject<AuthenticateUserResponse>();
            sessionID = authenticateUserResponse.SessionID;
        }

        /// <summary>
        /// Method viddler.users.getProfile 
        /// Retrieves the public parts of a user profile.
        /// </summary>
        public UserProfile GetUserProfile(string userName)
        {
            ViddlerRequest request = new ViddlerRequest(ViddlerMethod.GetUserProfile, apiKey, baseUrl);
            request.Parameters.Add("user", userName);
            ViddlerResponse response = request.GetResponse();

            return response.GetResponseObject<UserProfile>();
        }

        /// <summary>
        /// Method viddler.users.setProfile (since v2.3) 
        /// Updates authenticated user profile data
        /// </summary>
        public UserProfile SetUserProfile(SetUserProfileRequest setUserProfileRequest)
        {
            if (!IsAuthenticated)
            {
                throw new AuthenticationException("This method requires the current user to be authenticated. Call AuthenticateUser() first.");
            }

            ViddlerRequest request = new ViddlerRequest(ViddlerMethod.SetUserProfile, apiKey, baseUrl);
            request.SessionID = sessionID;

            if (setUserProfileRequest.FirstName != null) request.Parameters.Add("first_name", setUserProfileRequest.FirstName);
            if (setUserProfileRequest.LastName != null) request.Parameters.Add("last_name", setUserProfileRequest.LastName);
            if (setUserProfileRequest.AboutMe != null) request.Parameters.Add("about_me", setUserProfileRequest.AboutMe);
            if (setUserProfileRequest.Birthdate != null) request.Parameters.Add("birthdate", ((DateTime)setUserProfileRequest.Birthdate).ToString("yyyy-MM-dd"));
            if (setUserProfileRequest.Gender != null) request.Parameters.Add("gender", setUserProfileRequest.Gender);
            if (setUserProfileRequest.Company != null) request.Parameters.Add("company", setUserProfileRequest.Company);
            if (setUserProfileRequest.City != null) request.Parameters.Add("city", setUserProfileRequest.City);
            
            ViddlerResponse response = request.GetResponse();

            return response.GetResponseObject<UserProfile>();
        }

        /// <summary>
        /// Method viddler.users.setOptions 
        /// Sets user account options. Currently only partners options are supported. More of them later! 
        /// </summary>
        public int SetUserOptions(SetUserOptionsRequest setUserOptionsRequest)
        {
            if (!IsAuthenticated)
            {
                throw new AuthenticationException("This method requires the current user to be authenticated. Call AuthenticateUser() first.");
            }

            ViddlerRequest request = new ViddlerRequest(ViddlerMethod.SetUserOptions, apiKey, baseUrl);
            request.SessionID = sessionID;

            if (setUserOptionsRequest.ShowAccount != null) request.Parameters.Add("show_account", Convert.ToInt32(setUserOptionsRequest.ShowAccount));
            if (setUserOptionsRequest.TaggingEnabled != null) request.Parameters.Add("tagging_enabled", Convert.ToInt32(setUserOptionsRequest.TaggingEnabled));
            if (setUserOptionsRequest.CommentingEnabled != null) request.Parameters.Add("commenting_enabled", Convert.ToInt32(setUserOptionsRequest.CommentingEnabled));
            if (setUserOptionsRequest.ShowRelatedVideos != null) request.Parameters.Add("show_related_videos", Convert.ToInt32(setUserOptionsRequest.ShowRelatedVideos));
            if (setUserOptionsRequest.EmbeddingEnabled != null) request.Parameters.Add("embedding_enabled", Convert.ToInt32(setUserOptionsRequest.EmbeddingEnabled));
            if (setUserOptionsRequest.ClickingThroughEnabled != null) request.Parameters.Add("clicking_through_enabled", Convert.ToInt32(setUserOptionsRequest.ClickingThroughEnabled));
            if (setUserOptionsRequest.EmailThisEnabled != null) request.Parameters.Add("email_this_enabled", Convert.ToInt32(setUserOptionsRequest.EmailThisEnabled));
            if (setUserOptionsRequest.TrackbacksEnabled != null) request.Parameters.Add("trackbacks_enabled", Convert.ToInt32(setUserOptionsRequest.TrackbacksEnabled));
            if (setUserOptionsRequest.FavouritesEnabled != null) request.Parameters.Add("favourites_enabled", Convert.ToInt32(setUserOptionsRequest.FavouritesEnabled));
            if (setUserOptionsRequest.CustomLogoEnabled != null) request.Parameters.Add("custom_logo_enabled", Convert.ToInt32(setUserOptionsRequest.CustomLogoEnabled));

            ViddlerResponse response = request.GetResponse();

            return response.GetResponseAsInteger();
        }

        /// <summary>
        /// Method viddler.videos.getRecordToken 
        /// Generate token for embedded recorder 
        /// </summary>
        public string GetRecordToken()
        {
            if (!IsAuthenticated)
            {
                throw new AuthenticationException("This method requires the current user to be authenticated. Call AuthenticateUser() first.");
            }

            ViddlerRequest request = new ViddlerRequest(ViddlerMethod.GetRecordToken, apiKey, baseUrl);
            request.SessionID = sessionID;

            ViddlerResponse response = request.GetResponse();

            return response.GetResponseAsString();
        }

        /// <summary>
        /// Method viddler.videos.upload 
        /// Uploads a file from the FileInfo to a user’s account on Viddler site. NOTE: This call will be sent as a
        /// POST using the Content-Type multipart/form-data according to RFC1867. 
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="title"></param>
        /// <param name="tags"></param>
        /// <param name="description"></param>
        /// <param name="makePublic"></param>
        /// <param name="stream"></param>
        /// <returns>Video details</returns>
        public VideoDetails UploadVideo(string title, string tags, 
            string description, bool makePublic, FileInfo fileInfo)
        {
            return UploadVideo(fileInfo.Name, title, tags, description, makePublic, fileInfo.OpenRead());
        }

        /// <summary>
        /// Method viddler.videos.upload 
        /// Uploads a System.IO.Stream to a user’s account on Viddler site. NOTE: This call will be sent as a
        /// POST using the Content-Type multipart/form-data according to RFC1867. 
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="title"></param>
        /// <param name="tags"></param>
        /// <param name="description"></param>
        /// <param name="makePublic"></param>
        /// <param name="stream"></param>
        /// <returns>Video details</returns>
        public VideoDetails UploadVideo(string fileName, string title, string tags,
          string description, bool makePublic, Stream stream)
        {
            byte[] video = new byte[stream.Length];

            stream.Seek(0, SeekOrigin.Begin);
            stream.Read(video, 0, video.Length);

            return UploadVideo(fileName, title, tags, description, makePublic, video);
        }

        /// <summary>
        /// Method viddler.videos.upload 
        /// Uploads a byte array to a user’s account on Viddler site. NOTE: This call will be sent as a
        /// POST using the Content-Type multipart/form-data according to RFC1867. 
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="title"></param>
        /// <param name="tags"></param>
        /// <param name="description"></param>
        /// <param name="makePublic"></param>
        /// <param name="stream"></param>
        /// <returns>Video details</returns>
        public VideoDetails UploadVideo(string fileName, string title, string tags, 
            string description, bool makePublic, byte []data)
        {
            if (!IsAuthenticated)
            {
                throw new AuthenticationException("This method requires the current user to be authenticated. Call AuthenticateUser() first.");
            }

            ViddlerRequest request = new ViddlerRequest(ViddlerMethod.UploadVideo, apiKey, baseUrl);
            request.SessionID = sessionID;
            request.Parameters.Add("title", title);
            request.Parameters.Add("tags", tags);
            request.Parameters.Add("description", description);
            request.Parameters.Add("make_public", makePublic ==  false ? "0" : "1");

            ViddlerResponse response = request.UploadFile(fileName, data);

            return response.GetResponseObject<VideoDetails>();
        }

        /// <summary>
        /// Method viddler.videos.getStatus 
        /// Returns the status of a video uploaded through the API. 
        /// </summary>
        public GetVideoStatusResponse GetVideoStatus(string videoID)
        {
            ViddlerRequest request = new ViddlerRequest(ViddlerMethod.GetVideoStatus, apiKey, baseUrl);

            if (IsAuthenticated)
            {
                request.SessionID = sessionID;
            }

            request.Parameters.Add("video_id", videoID);
            ViddlerResponse response = request.GetResponse();

            return response.GetResponseObject<GetVideoStatusResponse>();
        }

        /// <summary>
        /// Method viddler.videos.getDetails (updated in v2.3) 
        /// Displays the details for a video.
        /// NOTE: currently addEmbedCode and includeComments parameters can be provided, but
        /// it won't change the result output.
        /// </summary>
        public VideoDetails GetVideoDetails(string videoID, bool addEmbedCode, bool includeComments)
        {
            ViddlerRequest request = new ViddlerRequest(ViddlerMethod.GetVideoDetails, apiKey, baseUrl);

            if (IsAuthenticated)
            {
                request.SessionID = sessionID;
            }

            request.Parameters.Add("video_id", videoID);
            request.Parameters.Add("add_embed_code", Convert.ToInt32(addEmbedCode));
            request.Parameters.Add("include_comments", Convert.ToInt32(includeComments));
            ViddlerResponse response = request.GetResponse();

            return response.GetResponseObject<VideoDetails>();
        }

        /// <summary>
        /// Method viddler.videos.getDetailsByUrl (since v2.3.1) 
        /// Displays the details for a video.
        /// </summary>
        public VideoDetails GetVideoDetails(string url)
        {
            ViddlerRequest request = new ViddlerRequest(ViddlerMethod.GetVideoDetailsByUrl, apiKey, baseUrl);

            if (IsAuthenticated)
            {
                request.SessionID = sessionID;
            }

            request.Parameters.Add("url", url);
            ViddlerResponse response = request.GetResponse();

            return response.GetResponseObject<VideoDetails>();
        }

        /// <summary>
        /// Method viddler.videos.setDetails (since v2.3) 
        /// Update video details. 
        /// </summary>
        public void SetVideoDetails()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Method viddler.videos.setPermalink
        /// Set permalink on videos you own. Permalink is used by the Flash player. When the user
        /// clicks on a video while it is playing, he will be redirected to the permalink url
        /// defined on the video.
        /// </summary>
        public void SetPermalink(string videoID, string permalink)
        {
            if (!IsAuthenticated)
            {
                throw new AuthenticationException("This method requires the current user to be authenticated. Call AuthenticateUser() first.");
            }

            ViddlerRequest request = new ViddlerRequest(ViddlerMethod.GetVideoDetailsByUrl, apiKey, baseUrl);
            request.Parameters.Add("video_id", videoID);
            request.Parameters.Add("permalink", permalink);
            ViddlerResponse response = request.GetResponse();
        }

        /// <summary>
        /// Method viddler.videos.getByUser 
        /// Lists all videos that were uploaded by the specified user. 
        /// </summary>
        public VideoList GetVideosByUser(string userName, int page, int perPage)
        {
            if (page < 1)
            {
                throw new ArgumentException("Page must be a non negative number starting at 1", "page");
            }

            ViddlerRequest request = new ViddlerRequest(ViddlerMethod.GetVideosByUser, apiKey, baseUrl);

            if (IsAuthenticated)
            {
                request.SessionID = sessionID;
            }

            request.Parameters.Add("user", userName);
            request.Parameters.Add("page", page);
            request.Parameters.Add("per_page", perPage);
            ViddlerResponse response = request.GetResponse();

            return response.GetResponseObject<VideoList>();
        }

        public VideoList GetVideosByUser(string userName, int page)
        {
            return GetVideosByUser(userName, page, 20);
        }

        public VideoList GetVideosByUser(string userName)
        {
            return GetVideosByUser(userName, 1);
        }

        /// <summary>
        /// Method viddler.videos.getByTag 
        /// Lists all videos that have the specified tag. 
        /// </summary>
        public VideoList GetVideosByTag(string tag, int page, int perPage)
        {
            ViddlerRequest request = new ViddlerRequest(ViddlerMethod.GetVideosByTag, apiKey, baseUrl);

            if (IsAuthenticated)
            {
                request.SessionID = sessionID;
            }

            request.Parameters.Add("tag", tag);
            request.Parameters.Add("page", page);
            request.Parameters.Add("per_page", perPage);
            ViddlerResponse response = request.GetResponse();

            return response.GetResponseObject<VideoList>();
        }

        public VideoList GetVideosByTag(string tag, int page)
        {
            return GetVideosByTag(tag, page, 20);
        }

        public VideoList GetVideosByTag(string tag)
        {
            return GetVideosByTag(tag, 1);
        }

        /// <summary>
        /// Method viddler.videos.getFeatured 
        /// Lists currently featured videos. 
        /// </summary>
        public VideoList GetFeaturedVideos()
        {
            ViddlerRequest request = new ViddlerRequest(ViddlerMethod.GetFeaturedVideos, apiKey, baseUrl);

            ViddlerResponse response = request.GetResponse();

            return response.GetResponseObject<VideoList>();
        }

        /// <summary>
        /// Method viddler.videos.comments.add
        /// Lists all videos that have the specified tag. 
        /// </summary>
        public Comment AddComment(string videoID, string text)
        {
            ViddlerRequest request = new ViddlerRequest(ViddlerMethod.AddComment, apiKey, baseUrl);

            if (!IsAuthenticated)
            {
                throw new AuthenticationException("This method requires the current user to be authenticated. Call AuthenticateUser() first.");
            }

            request.Parameters.Add("video_id", videoID);
            request.Parameters.Add("text", text);
            ViddlerResponse response = request.GetResponse();

            return response.GetResponseObject<Comment>();
        }

        /// <summary>
        /// Method viddler.videos.comments.remove 
        /// Lists all videos that have the specified tag. 
        /// </summary>
        public void RemoveComment(string commentID)
        {
            ViddlerRequest request = new ViddlerRequest(ViddlerMethod.RemoveComment, apiKey, baseUrl);

            if (!IsAuthenticated)
            {
                throw new AuthenticationException("This method requires the current user to be authenticated. Call AuthenticateUser() first.");
            }

            request.Parameters.Add("comment_id", commentID);
            ViddlerResponse response = request.GetResponse();
        }
    }
}